package com.cloud.generator.service.impl;

import com.alibaba.fastjson2.JSON;
import com.cloud.common.configure.GenProperties;
import com.cloud.common.constant.GenConstants;
import com.cloud.common.constants.GlobalConstants;
import com.cloud.common.util.GenUtils;
import com.cloud.common.util.InitVelocityUtils;
import com.cloud.common.util.VelocityUtils;
import com.cloud.exception.CustomException;
import com.cloud.generator.domain.GenTable;
import com.cloud.generator.domain.GenTableColumn;
import com.cloud.generator.mapper.GenTableColumnMapper;
import com.cloud.generator.mapper.GenTableMapper;
import com.cloud.generator.service.IGenTableService;
import com.cloud.utils.SecurityUtils;
import com.cloud.utils.ServletUtils;
import com.cloud.utils.StringUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 业务/服务层实现
 *
 * @author yzj
 */
@Service
public class GenTableServiceImpl implements IGenTableService {
    private static final Logger logger = LoggerFactory.getLogger(GenTableServiceImpl.class);

    @Resource
    private GenTableMapper genTableMapper;

    @Resource
    private GenTableColumnMapper genTableColumnMapper;

    @Resource
    private GenProperties genProperties;

    /**
     * 查询业务信息
     *
     * @param id 业务ID
     * @return 业务信息
     */
    @Override
    public GenTable selectGenTableById(Long id) {
        return genTableMapper.selectGenTableById(id);
    }

    /**
     * 查询业务列表
     *
     * @param genTable 业务信息
     * @return 业务集合
     */
    @Override
    public List<GenTable> selectGenTableList(GenTable genTable) {
        return genTableMapper.selectGenTableList(genTable);
    }

    /**
     * 查询据库列表
     *
     * @param genTable 业务信息
     * @return 数据库表集合
     */
    @Override
    public List<GenTable> selectDbTableList(GenTable genTable) {
        return genTableMapper.selectDbTableList(genTable);
    }

    /**
     * 查询据库列表
     *
     * @param tableNames 表名称组
     * @return 数据库表集合
     */
    @Override
    public List<GenTable> selectTableListByNames(List<String> tableNames) {
        return genTableMapper.selectTableListByNames(tableNames);
    }

    /**
     * 查询所有表信息
     *
     * @return 表信息集合
     */
    @Override
    public List<GenTable> selectAllGenTable() {
        return genTableMapper.selectAllGenTable();
    }

    /**
     * 修改业务
     *
     * @param genTable 业务信息
     * @return 结果
     */
    @Override
    public void updateGenTable(GenTable genTable) {
        String options = JSON.toJSONString(genTable.getAskParam());
        genTable.setOptions(options);
        int row = genTableMapper.updateGenTable(genTable);
        if (row > 0) {
            for (GenTableColumn cenTableColumn : genTable.getColumns()) {
                genTableColumnMapper.updateGenTableColumn(cenTableColumn);
            }
        }
    }

    /**
     * 删除业务对象
     *
     * @param tableIds 需要删除的数据ID
     * @return 结果
     */
    @Override
    public void deleteGenTableByIds(Long[] tableIds) {
        genTableMapper.deleteGenTableByIds(tableIds);
        genTableColumnMapper.deleteGenTableColumnByIds(tableIds);
    }

    /**
     * 导入表结构
     *
     * @param tableList 导入表列表
     */
    @Override
    public void importGenTable(List<GenTable> tableList) {
        try {
            String userName = SecurityUtils.getUserName();
            for (GenTable table : tableList) {
                table.setTablePrefix(genProperties.getTablePrefix());
                table.setPackageName(genProperties.getPackageName());
                table.setModuleName(genProperties.getModuleName());
                table.setFunctionAuthor(genProperties.getAuthor());
                // 初始化表信息
                GenUtils.initTable(table, userName);
                int row = genTableMapper.insertGenTable(table);
                if (row > 0) {
                    // 保存归属表列信息
                    List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(table.getTableName());
                    for (GenTableColumn column : genTableColumns) {
                        // 初始化列属性字段
                        GenUtils.initColumnField(column, table);
                        genTableColumnMapper.insertGenTableColumn(column);
                    }
                }
            }
        } catch (Exception e) {
            throw new CustomException("导入失败：" + e.getMessage());
        }
    }

    /**
     * 预览代码
     *
     * @param tableId 表编号
     * @return 预览数据列表
     */
    @Override
    public Map<String, String> previewCode(Long tableId) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        GenTable table = genTableMapper.selectGenTableById(tableId);
        if (StringUtils.isNotNull(table)) {
            // 设置主子表信息
            setSubTable(table);
            // 设置主键列信息
            setPkColumn(table);
            InitVelocityUtils.initVelocity();
            // 获取模板列表
            VelocityContext context = VelocityUtils.prepareContext(table);
            List<String> templates = VelocityUtils.getTemplateList(table);
            for (String template : templates) {
                StringWriter sw = new StringWriter();
                Template tpl = Velocity.getTemplate(template, GlobalConstants.UTF_8);
                tpl.merge(context, sw);
                dataMap.put(template, sw.toString());
            }
        }
        return dataMap;
    }

    /**
     * 生成代码（下载方式）
     *
     * @param tableName 表名称
     * @return 数据
     */
    @Override
    public void downloadCode(String tableName) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        try {
            GenTable table = genTableMapper.selectGenTableByName(tableName);
            if (StringUtils.isNotNull(table)) {
                checkSubTable(table);
                // 设置主子表信息
                setSubTable(table);
                // 设置主键列信息
                setPkColumn(table);
                // 初始化模板列表
                InitVelocityUtils.initVelocity();
                VelocityContext context = VelocityUtils.prepareContext(table);
                List<String> templates = VelocityUtils.getTemplateList(table);
                for (String template : templates) {
                    StringWriter sWriter = new StringWriter();
                    Template tpl = Velocity.getTemplate(template, GlobalConstants.UTF_8);
                    tpl.merge(context, sWriter);
                    zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                    IOUtils.write(sWriter.toString(), zip, GlobalConstants.UTF_8);
                    IOUtils.closeQuietly(sWriter);
                    zip.flush();
                    zip.closeEntry();
                }
            }
        } catch (Exception e) {
            logger.error("渲染模板失败,表名:" + tableName, e);
        }
        IOUtils.closeQuietly(zip);
        genCodeByZip(outputStream.toByteArray());
    }

    /**
     * 生成代码（自定义路径）
     *
     * @param tableName 表名称
     */
    @Override
    public void setGeneratorCode(String tableName) {
        try {
            GenTable table = genTableMapper.selectGenTableByName(tableName);
            if (StringUtils.isNotNull(table)) {
                checkSubTable(table);
                // 设置主子表信息
                setSubTable(table);
                // 设置主键列信息
                setPkColumn(table);
                // 初始化模板列表
                InitVelocityUtils.initVelocity();
                VelocityContext context = VelocityUtils.prepareContext(table);
                List<String> templates = VelocityUtils.getTemplateList(table);
                for (String template : templates) {
                    StringWriter sWriter = new StringWriter();
                    Template tpl = Velocity.getTemplate(template, GlobalConstants.UTF_8);
                    tpl.merge(context, sWriter);
                    // 创建生成地址,并写入文件
                    String pathUrl = getGenPath(table, template);
                    if (StringUtils.isNotEmpty(pathUrl)) {
                        FileUtils.writeStringToFile(new File(pathUrl), sWriter.toString(), GlobalConstants.UTF_8);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("渲染模板失败,表名:" + tableName, e);
        }
    }

    /**
     * 批量生成代码（下载方式）
     *
     * @param tableNames 表数组
     * @return 数据
     */
    @Override
    public void downloadCode(List<String> tableNames) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames) {
            try {
                GenTable table = genTableMapper.selectGenTableByName(tableName);
                if (StringUtils.isNotNull(table)) {
                    checkSubTable(table);
                    // 设置主子表信息
                    setSubTable(table);
                    // 设置主键列信息
                    setPkColumn(table);
                    // 初始化模板列表
                    InitVelocityUtils.initVelocity();
                    VelocityContext context = VelocityUtils.prepareContext(table);
                    List<String> templates = VelocityUtils.getTemplateList(table);
                    for (String template : templates) {
                        StringWriter sWriter = new StringWriter();
                        Template tpl = Velocity.getTemplate(template, GlobalConstants.UTF_8);
                        tpl.merge(context, sWriter);
                        zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
                        IOUtils.write(sWriter.toString(), zip, GlobalConstants.UTF_8);
                        IOUtils.closeQuietly(sWriter);
                        zip.flush();
                        zip.closeEntry();
                    }
                }
            } catch (Exception e) {
                logger.error("渲染模板失败,表名:" + tableName, e);
            }
        }
        IOUtils.closeQuietly(zip);
        genCodeByZip(outputStream.toByteArray());
    }

    /**
     * 修改保存参数校验
     *
     * @param genTable 业务信息
     */
    @Override
    public void checkSubTable(GenTable genTable) {
        if (StringUtils.isEmpty(genTable.getPackageName())) {
            throw new CustomException("构建生成包路径不能为空");
        }
        if (StringUtils.isEmpty(genTable.getModuleName())) {
            throw new CustomException("构建生成模块名不能为空");
        }
        if (StringUtils.isEmpty(genTable.getBusinessName())) {
            throw new CustomException("构建生成业务名不能为空");
        }
        if (StringUtils.isEmpty(genTable.getFunctionName())) {
            throw new CustomException("构建生成功能名不能为空");
        }
        if (StringUtils.isEmpty(genTable.getFunctionAuthor())) {
            throw new CustomException("构建生成功能作者不能为空");
        }
        if (GenConstants.TREE_CODE.equals(genTable.getTplCategory())) {
            throw new CustomException("树编码字段不能为空");
        } else if (GenConstants.TREE_PARENT_CODE.equals(genTable.getTplCategory())) {
            throw new CustomException("树父编码字段不能为空");
        } else if (GenConstants.TREE_NAME.equals(genTable.getTplCategory())) {
            throw new CustomException("树名称字段不能为空");
        } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
            if (StringUtils.isEmpty(genTable.getSubTableName())) {
                throw new CustomException("关联子表的表名不能为空");
            } else if (StringUtils.isEmpty(genTable.getSubTableFkName())) {
                throw new CustomException("子表关联的外键名不能为空");
            }
        }
    }

    /**
     * 获取代码生成地址
     *
     * @param table    业务表信息
     * @param template 模板文件路径
     * @return 生成地址
     */
    private String getGenPath(GenTable table, String template) {
        // 创建前端文件路径
        if (StringUtils.isNotEmpty(table.getVueRoute())) {
            if (StringUtils.containsAny(template, "index.js.vm",
                    "index.vue.vm", "index-tree.vue.vm")) {
                return table.getVueRoute() + "/src/" + VelocityUtils.getFileName(template, table);
            }
        }
        // 创建后端文件路径
        if (StringUtils.isNotEmpty(table.getJavaRoute())) {
            if (StringUtils.containsAny(template, "controller.java.vm",
                    "domain.java.vm", "service.java.vm", "serviceImpl.java.vm", "mapper.java.vm", "mapper.xml.vm")) {
                return table.getJavaRoute() + "/" + VelocityUtils.getFileName(template, table);
            }
        }
        // 创建默认文件路径
        if (StringUtils.isNotEmpty(table.getGenPath())) {
            return table.getGenPath() + "/" + VelocityUtils.getFileName(template, table);
        }
        return null;
    }

    /**
     * 设置主键列信息
     *
     * @param table 业务表信息
     */
    private void setPkColumn(GenTable table) {
        for (GenTableColumn column : table.getColumns()) {
            if (column.checkPk()) {
                table.setPkColumn(column);
                break;
            }
        }
        if (StringUtils.isNull(table.getPkColumn())) {
            table.setPkColumn(table.getColumns().get(0));
        }
        if (GenConstants.TPL_SUB.equals(table.getTplCategory())) {
            for (GenTableColumn column : table.getSubTable().getColumns()) {
                if (column.checkPk()) {
                    table.getSubTable().setPkColumn(column);
                    break;
                }
            }
            if (StringUtils.isNull(table.getSubTable().getPkColumn())) {
                table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0));
            }
        }
    }

    /**
     * 设置主子表信息
     *
     * @param table 业务表信息
     */
    private void setSubTable(GenTable table) {
        String subTableName = table.getSubTableName();
        if (StringUtils.isNotEmpty(subTableName)) {
            table.setSubTable(genTableMapper.selectGenTableByName(subTableName));
        }
    }

    /**
     * 生成zip文件
     */
    private void genCodeByZip(byte[] data) {
        try {
            HttpServletResponse response = ServletUtils.getResponse();
            response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment; filename=generator.zip");
            response.addHeader("Content-Length", "" + data.length);
            response.setContentType("application/octet-stream; charset=UTF-8");
            IOUtils.write(data, response.getOutputStream());
        } catch (IOException e) {
            e.fillInStackTrace();
        }
    }

}